package server;

import command.server.AcceptInvitationServer;
import command.server.InviteUserToChatServer;
import command.server.SendMessageServer;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import model.Conversation;
import model.Message;
import model.User;
import persistence.ConversationDAO;
import persistence.PersistenceFactory;
import persistence.UserDAO;

//@Abraham sincronización de grano muy gordo; ver comentario de e-mail
public class Server {

    private ServerSocket serverSocket;
    private int PORT = 9000;
    private boolean serverActive = true;
    private static HashMap<String, Conversation> conversationsActive = new HashMap<>();
    private static HashMap<User, ClientThread> usersConnected = new HashMap<>();
    private String driverDB;
    private String urlDB;
    private String userDB;
    private String passDB;
    private String persistenceMechanism;
    private Properties configurationDB = new Properties();
    private static ConversationDAO persistenceManagerConversation;
    private static UserDAO persistenceManagerUser;

    public Server() {
        startServer();
    }

    public static void main(String args[]) {
        Server server = new Server();
    }

    public final void startServer() {
        Socket client = null;
        try {
            if (connectToDB()) {
                persistenceManagerUser.newUser(new User("admin", "admin", "admin", "admin", true));
                serverSocket = new ServerSocket(PORT);
                while (serverActive) {
                    client = serverSocket.accept();
                    ClientThread connection = new ClientThread(client);
                    connection.start();
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.WARNING,
                    "Hubo un problema al iniciar el servidor", ex);
        } finally {
            if (client != null) {
                try {
                    client.close();
                } catch (IOException ex) {
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE,
                            "Hubo un problema al cerrar la conexion con el cliente", ex);
                }
            }
        }
    }

    public void stopServer() {
        serverActive = false;
        persistenceManagerConversation.disconnect();
        persistenceManagerUser.disconnect();
        try {
            serverSocket.close();
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE,
                    "Fallo al cerrar el socket del Servidor", ex);
        }
    }

    private boolean connectToDB() {
        try {
            configurationDB.load(Server.class.getResourceAsStream("configuration.properties"));
            driverDB = configurationDB.getProperty("driver");
            urlDB = configurationDB.getProperty("url");
            userDB = configurationDB.getProperty("user");
            passDB = configurationDB.getProperty("pass");
            persistenceMechanism = configurationDB.getProperty("persistenceMechanism");
            persistenceManagerConversation = PersistenceFactory.getConversationDAO(persistenceMechanism);
            persistenceManagerUser = PersistenceFactory.getUserDAO(persistenceMechanism);
            boolean ok = persistenceManagerConversation.connection(userDB, passDB, driverDB, urlDB)
                    && persistenceManagerUser.connection(userDB, passDB, driverDB, urlDB);
            return ok;
        } catch (IOException ex) {
            Logger.getLogger(Server.class.getName()).log(Level.WARNING,
                    "Hubo un problema al crear la conexión con la Base de Datos", ex);
            return false;
        }
    }

    public static ConversationDAO getServerConversationDAO() {
        return persistenceManagerConversation;
    }

    public static UserDAO getServerUserDAO() {
        return persistenceManagerUser;
    }

    /*
     * Acciones sobre Usuarios:
     */
    public static synchronized void addUserActive(User u, ClientThread ct) {
        usersConnected.put(u, ct);
        sendListOfUser();
    }

    public static synchronized void removeUserActive(User u) {
        usersConnected.remove(u);
        Iterator it = conversationsActive.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry e = (Map.Entry) it.next();
            Conversation c = (Conversation) e.getValue();
            removeUserAtConversation(c.getConversationID(), u);
        }
        sendListOfUser();
    }

    public static synchronized ArrayList<User> getUserActiveList() {
        ArrayList<User> usersActive = new ArrayList<>();
        Iterator it = usersConnected.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry e = (Map.Entry) it.next();
            User u = (User) e.getKey();
            usersActive.add(u);
        }
        return usersActive;
    }

    public static synchronized void sendListOfUser() {
        Iterator it = usersConnected.entrySet().iterator();
        while (it.hasNext()) {
            Map.Entry e = (Map.Entry) it.next();
            ClientThread c = (ClientThread) e.getValue();
            c.executeCommand("getlistofusersserver");
        }
    }

    /*
     * Acciones sobre Conversaciones:
     */
    public static synchronized void addConversation(Conversation c) {
        if (!conversationsActive.containsKey(c.getConversationID())) {
            Date date = new Date();
            c.setConversationDateStart(date.toString());
            conversationsActive.put(c.getConversationID(), c);
        }
    }

    public static synchronized void sendInvitationToConversation(ArrayList<String> userList,
            String userInvites, String conversationID) {
        Iterator iteratorList = userList.iterator();
        while (iteratorList.hasNext()) {
            String userNickName = (String) iteratorList.next();
            Iterator iteratorHashMap = usersConnected.entrySet().iterator();
            while (iteratorHashMap.hasNext()) {
                Map.Entry e = (Map.Entry) iteratorHashMap.next();
                User u = (User) e.getKey();
                Conversation c = conversationsActive.get(conversationID);
                if (!c.getConversationUsersActive().contains(u)) {
                    if (u.getUserNickName().equals(userNickName)) {
                        ClientThread ct = (ClientThread) e.getValue();
                        InviteUserToChatServer command = new InviteUserToChatServer();
                        command.setIsInvitationAtTrue();
                        command.setConversationID(conversationID);
                        command.setUserInvites(userInvites);
                        command.execute(ct);
                    }
                }
            }
        }
    }

    public static synchronized void addMessageAtConversation(String conversationID, Message message) {
        Date date = new Date();
        message.setMessageDate(date.toString());
        if (conversationsActive.get(conversationID).addMessageAtConversation(message)) {
            Iterator it = conversationsActive.get(conversationID).getConversationUsersActive().iterator();
            while (it.hasNext()) {
                User u = (User) it.next();
                ClientThread ct = usersConnected.get(u);
                SendMessageServer command = new SendMessageServer();
                command.setIsSendMessage();
                command.setConversationID(conversationID);
                command.setMessageToAdd(message);
                command.execute(ct);
            }
        }
    }

    /*
     * Acciones sobre Usuarios y Conversacions:
     */
    public static synchronized void addUserAtConversation(String conversationID, User u) {
        if (conversationsActive.get(conversationID).addUserAtConversation(u)) {
            ArrayList<User> listOfUsers = new ArrayList<>(conversationsActive.get(conversationID).
                    getConversationUsersActive());
            sendListOfUserActiveAtConversation(conversationID, listOfUsers);
        }
    }

    public static synchronized void removeUserAtConversation(String conversationID, User u) {
        if (conversationsActive.get(conversationID).removeUserAtConversation(u)) {
            ArrayList<User> listOfUsers = new ArrayList<>(conversationsActive.get(conversationID).
                    getConversationUsersActive());
            sendListOfUserActiveAtConversation(conversationID, listOfUsers);
            if (conversationsActive.get(conversationID).getConversationUsersActive().isEmpty()) {
                Date date = new Date();
                conversationsActive.get(conversationID).setConversationDateEnd(date.toString());
                persistenceManagerConversation.newConversation(conversationsActive.get(conversationID));
                conversationsActive.remove(conversationID);
            }
        }
    }

    public static synchronized void sendListOfUserActiveAtConversation(String conversationID,
            ArrayList<User> usersActive) {
        Iterator it = conversationsActive.get(conversationID).getConversationUsersActive().iterator();
        while (it.hasNext()) {
            User u = (User) it.next();
            ClientThread ct = usersConnected.get(u);
            AcceptInvitationServer command = new AcceptInvitationServer();
            command.setConversationID(conversationID);
            command.setUserActive(usersActive);
            command.setIsAcceptConversation();
            command.execute(ct);
        }
    }
}
